home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993…ch: Other People's Memory / ADC Developer CD (1993-03) (''Other People's Memory'')_iso / Dev.CD Mar 93.iso / Technical Documentation / Sample Code / DTS_SCSI Code (In Development) / DTS_SCSI_Driver.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-15  |  16.8 KB  |  496 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        DTS_SCSI_Driver.c
  3.     
  4.     
  5.     
  6.     
  7.     Unfortunately, no matter how long awaited, it's still not done.  In fact, this
  8. isn't even a release- this is just an image of the code taken in the middle of
  9. development.
  10.  
  11. THIS CODE DOES NOT WORK AS A WHOLE.  MUCH OF IT IS BUGGY AND / OR INCOMPLETE.
  12. YOU WOULD HAVE TO BE ABSOLUTELY INSANE TO USE ANY OF THIS CODE IN YOUR
  13. PROJECT WITHOUT EXTENSIVE THOUGHT, DEBUGGING AND TESTING.
  14.  
  15.  
  16.  
  17.  
  18.  
  19.     Contains:    main driver routines
  20.  
  21.     Written by:    Craig Prouse
  22.  
  23.     Copyright:    © 1991-1992 by Apple Computer, Inc., all rights reserved.
  24.  
  25.     Change History (most recent first):
  26.  
  27.          <9>     4/24/92    tmd        Code review stuff; lots of nitpicking on comments & format
  28.          <8>      3/6/92    chp        fix more bugs: eliminate drive queue entry when closing driver;
  29.                                     clean up comments and generally pick nits
  30.          <7>    11/26/91    chp        fix bugs: wait until after initializing dCtlStorage to set up a
  31.                                     pointer to driver variables for the Open routine, allocate
  32.                                     dCtlStorage low in the system heap and *lock* it there, skip
  33.                                     drive validation on accRun calls, clear the correct bit of
  34.                                     dCtlFlags to cancel accRuns (was setting the correct bit in the
  35.                                     wrong byte), and remove debugger asserts for unimplemented
  36.                                     status and control calls (we can expect these)
  37.          <6>    11/25/91    chp        roll in changes from old DrvrUtils module, add support for
  38.                                     volume validation, break out a couple of common code segments
  39.                                     into separate functions for ease of maintenance, and make the
  40.                                     accRun/eject support more comprehensible
  41.          <5>    10/25/91    chp        DriveIcon was declared as a function for convenience. Actually,
  42.                                     it's a block of constant data. Redeclare it more properly as a
  43.                                     generic external entity.
  44.          <4>    10/16/91    chp        complete implementations of the accRun and eject Control calls,
  45.                                     and complete implementation of the Open routine by adding code
  46.                                     to install us in the drive queue
  47.          <3>    10/16/91    chp        update to reflect changes in our headers
  48.          <2>    10/15/91    chp        update header contents
  49.          <1>    10/15/91    chp        first checked in
  50.  
  51.     To Do:
  52. */
  53.  
  54. #define __FILE_NUMBER__        0x1000  //*** Change to a kconstant.
  55.  
  56. #include    <Traps.h>
  57. #include    <SysEqu.h>
  58. #include    <Traps.h>
  59. #include    <Errors.h>
  60. #include    <Desk.h>
  61. #include    <Memory.h>
  62. #include    <OSEvents.h>
  63. #include    <Files.h>
  64. #include    <Devices.h>
  65. #include    "DTS_SCSI_Debug.h"
  66. #include    "DTS_SCSI_Driver.h"
  67. #include    "DTS_SCSI_IO.h"
  68.  
  69. // 
  70. // Our icon, included by DTS_SCSI_Driver.a as a block of constant data
  71. // 
  72. extern short DriveIcon[];
  73.  
  74. // 
  75. // What kind of I/O request is this?
  76. // It's either synchronous, asynchronous, or immediate, and we can
  77. // tell by checking the bits in the trap word that got us here (the
  78. // Device Manager kindly copied the trap word into the parameter block
  79. // as part of its dispatching).
  80. //
  81. typedef enum TIOReqType { kReqSync, kReqAsync, kReqImmed } TIOReqType;
  82. TIOReqType IORequestType(ParmBlkPtr pb) {
  83.     unsigned short    trap = pb->ioParam.ioTrap;
  84.     TIOReqType        typeOfIOReq;
  85.  
  86.     enum {                        // Bits set in the trap word
  87.         noQueueBit = 9,            // IMMED bit
  88.         asyncTrpBit = 10        // ASYNC bit
  89.     };
  90.  
  91.     if (trap & (1 << noQueueBit))
  92.         typeOfIOReq = kReqImmed;
  93.     else if (trap & (1 << asyncTrpBit))
  94.         typeOfIOReq = kReqAsync;
  95.     else
  96.         typeOfIOReq = kReqSync;
  97.     
  98.     return typeOfIOReq;
  99. }
  100.  
  101.  
  102. // 
  103. // Which kind of Prime operation is this?
  104. // We figure it out from the trap word in the parameter block;
  105. // we mask off the bits that complicate trap words, and we
  106. // should be left with _Read or _Write.
  107. //
  108. typedef enum { kPrimeRead, kPrimeWrite } EPrimeType;
  109.  
  110. EPrimeType PrimeType(ParmBlkPtr pb) {
  111.     const unsigned short osTrapFlagsMask = 0xF0FF;
  112.     unsigned short trap = pb->ioParam.ioTrap & osTrapFlagsMask;
  113.     EPrimeType typeOfPrime;
  114.     
  115.     if (trap == _Read & osTrapFlagsMask)
  116.         typeOfPrime = kPrimeRead;
  117.     else if (trap == _Write & osTrapFlagsMask)
  118.         typeOfPrime = kPrimeWrite;
  119.     else
  120.         assert(false, "DTS_SCSI PrimeType: trap not read or write");
  121.     
  122.     return typeOfPrime;
  123. }
  124.  
  125.  
  126. // 
  127. // The SCSI Manager supports Blind transfers, but they're unreliable on
  128. // the unpatched Macintosh Plus ROM (once the Macintosh Plus boots, they're 
  129. // safe; they're also always safe on all other Macintoshes with SCSI).
  130. //
  131. Boolean BlindIsSafe() {            //*** Need a better way to check if blind is safe
  132.     const unsigned short kMacPlusROMVersion = 0x0075; 
  133.     
  134.     //
  135.     // We check for Macintosh-Plus-ness by looking at the ROM version (8 bytes into 
  136.     // the ROM) -- note that this is not a good way to see what a machine's ROM version
  137.     // is. Gestalt would be better, but the whole point here is that we use this routine
  138.     // before the System has been opened, and Gestalt isn't around yet on the Macintosh
  139.     // Plus.
  140.     // 
  141.     // Once we know it's a Plus, we see if _SCSIDispatch has been patched by getting
  142.     // its address and seeing if it's above the start of the ROM. If so, we're not patched
  143.     // yet.
  144.     //
  145.     if (*(short *) (*(Ptr *) ROMBase + 8) == kMacPlusROMVersion) {
  146.         if (NGetTrapAddress(_SCSIDispatch, OSTrap) >= *(long *) ROMBase) {
  147.             return false; // don't do blind transfers!
  148.         }
  149.     }
  150.     
  151.     return true; // blind transfers are OK.
  152. }
  153.  
  154.  
  155. //
  156. // If this drive number is ours, return true.
  157. // Otherwise, return false.
  158. //
  159. Boolean IsValidDrive(TDriveVars *vars, short theDrive) {
  160.     //
  161.     // Is it our drive? Compare this drive number with the one in our
  162.     // drive queue element.
  163.     //
  164.     return (vars->driveQElem.elem.dQDrive == theDrive);
  165. }
  166.  
  167.  
  168. // 
  169. // If this drive number is valid, return the size of our drive, in blocks.
  170. // Otherwise return 0.
  171. //
  172. unsigned long DriveSize(TDriveVars *vars, short theDrive) {
  173.     //
  174.     // First, make sure this is our drive
  175.     //
  176.     if (!IsValidDrive(vars, theDrive))
  177.         return 0;
  178.     
  179.     // It's our drive. Now, about that size...
  180.     // The size (in blocks) of a given drive is stored in the drive queue element
  181.     // fields dqDrv and dqDrv2. Originally, there wasn't a dqDrv2, and we were limited
  182.     // to drives with 32M or less (I feel old just telling you this… "When I was your age…"),
  183.     // so the extra field was added. Here, we put 'em together and return it as one big number.
  184.     return vars->driveQElem.elem.dQDrvSz | (vars->driveQElem.elem.dQDrvSz2 << 16);
  185. }
  186.  
  187.  
  188. //
  189. // Is this boot time?
  190. //
  191. Boolean IsBootTime() {
  192.     //
  193.     // One characteristic of boot time is that SysEvtMask is zero
  194.     // *** This is the '90s - there's got to be a better way to check for this!
  195.     //
  196.     return *(short *) SysEvtMask == 0;
  197. }
  198.  
  199. // 
  200. // Open our driver. This routine is called by the system when the driver is
  201. // installed, either at boot time or by the Formatter when the drive is newly
  202. // initialized.
  203. //
  204. // Our job at "open" time is to set up a drive queue element (along with some
  205. // other state information) for our one-and-only drive. We don't need to mount
  206. // the drive at boot time because the system knows enough to try to boot from it;
  207. // however, if that try doesn't succeed (because there's no system folder, etc.),
  208. // we'll need to sneak around to get it mounted as soon as the system finishes
  209. // starting up: this is what the "tickleFlag" state variable is for.
  210. //
  211. OSErr OpenFunc(ParmBlkPtr pb, DCtlPtr dce) {
  212. #pragma unused (pb)
  213.     TDriveVars    *vars;
  214.     DrvQEl        *aDrive;
  215.     short        tryDriveNum;
  216.     
  217.     //
  218.     // Allocate storage for our state. We lock it down and leave it that
  219.     // way, because it contains a drive queue element (which can never move
  220.     // once we've added it to the drive queue, which we're about to do.)
  221.     //
  222.     ReserveMemSys(sizeof(TDriveVars));
  223.     dce->dCtlStorage = NewHandleSysClear(sizeof(TDriveVars));
  224.     assert(dce->dCtlStorage, "DTS_SCSI OpenFunc: unable to allocate dCtlStorage");
  225.     
  226.     // If we couldn't get the space, just return the error code (leaving dCtlStorage NULL)
  227.     if (dce->dCtlStorage == NULL)
  228.         return MemError();            
  229.     
  230.     // We got the storage. Remember where it is, and lock it down.
  231.     HLock(dce->dCtlStorage);
  232.     vars = *(TDriveVars **) dce->dCtlStorage;
  233.         
  234.     //
  235.     // Now we need to find a drive number for our drive. We're going to look
  236.     // through the drive queue, and use the first available number greater than
  237.     // four (the lower numbers are reserved for the floppy drives and the old HD20).
  238.     //
  239.     tryDriveNum = 5;
  240.     aDrive = (DrvQEl *) GetDrvQHdr()->qHead; // get the head of the drive queue
  241.     while (aDrive != NULL) {
  242.         if (aDrive->dQDrive == tryDriveNum) { 
  243.             // Oops, someone's already using this drive number.
  244.             tryDriveNum++; // try next drive number
  245.             aDrive = (DrvQEl *) GetDrvQHdr()->qHead; // start again at the beginning of the queue
  246.         } else {
  247.             // Good so far--check against the next drive in the queue.
  248.             aDrive = (DrvQEl *) aDrive->qLink;
  249.         }
  250.     }
  251.     // We fall out of the loop when we've made it all the way to the end of the queue without 
  252.     // finding an existing drive queue element using our number. So, at this point, tryDriveNum
  253.     // contains a unique drive number for us to use.
  254.     vars->driveQElem.elem.dQDrive = tryDriveNum; // Remember it in our drive state
  255.     
  256.     // Can we do blind transfers? We check here in case we're running on a MacPlus
  257.     // where the code in ROM won't do them right; we'll check again when we get an
  258.     // accRun call (once the Finder's up) - at that point, even the Plus has them
  259.     // working correctly.
  260.     vars->blindOK = BlindIsSafe();
  261.     
  262.     // 
  263.     // We've got a drive number - fill in the rest of our drive queue entry (it was zeroed when
  264.     // we allocated it, so we just have to deal with the nonzero stuff), then tell the 
  265.     // system about it with AddDrive.
  266.     //
  267.     vars->driveQElem.flags[1] = noEjectFlag;    // nonejectable media flag
  268.     vars->driveQElem.elem.qType = 1;            // drive size stored in both dQDrvSz and dQDrvSz2
  269.     AddDrive(dce->dCtlRefNum, tryDriveNum, &vars->driveQElem.elem);
  270.     
  271.     return noErr; // "Yes! We're Open"
  272. }
  273.  
  274.  
  275. // 
  276. // Prime means "Read" and "Write", which is what we're s'posed to do here.
  277. // We've got a low-level routine that takes care of most of the tough stuff,
  278. // so all we have to do is verify that the drive number and the size of the
  279. // request are legit.
  280. //
  281. OSErr PrimeFunc (ParmBlkPtr pb, DCtlPtr dce)
  282. {
  283.     OSErr result;
  284.     unsigned long blockSize, firstLogicalBlock, firstPhysicalBlock, nBlocks;
  285.     unsigned long partitionSize;
  286.     TDriveVars *vars = *(TDriveVars **) dce->dCtlStorage;
  287.     
  288.     // How big's our drive?
  289.     partitionSize = DriveSize(vars, pb->ioParam.ioVRefNum); 
  290.     
  291.     // If the size we got isn't positive, this ain't our drive. Return an error
  292.     if (partitionSize <= 0)
  293.         return nsvErr;
  294.     
  295.     // 
  296.     // The drive number is good. Can we satisfy the request? We start with the
  297.     // block offset to the start of the partition, and offset it by the block
  298.     // address of the start of the request (which came to us as a byte count;
  299.     // a quick divide turns this into a block count).
  300.     //
  301.     blockSize = vars->driveBlockSize;
  302.     nBlocks = pb->ioParam.ioReqCount / blockSize; // how many blocks?
  303.     firstLogicalBlock = dce->dCtlPosition / blockSize; // first requested block in our partition
  304.     firstPhysicalBlock = vars->partitionOffset + firstLogicalBlock; // offset to physical block
  305.     
  306.     // Theoretically, we only get called with requests that start at a block boundary,
  307.     // and ask for an even number of blocks. Just to be safe, we check this.
  308.     assert(dce->dCtlPosition % blockSize == 0,"DTS_SCSI PrimeFunc: Starting at weird offset");
  309.     assertv(pb->ioParam.ioReqCount % blockSize == 0,pb->ioParam.ioReqCount % blockSize);
  310.     
  311.     // 
  312.     // Sanity check: the request must be nonempty and within the boundaries of
  313.     // our partition. If it's sane, do the transfer.
  314.     //
  315.     if ((nBlocks > 0) && (firstLogicalBlock + nBlocks <= partitionSize)) {
  316.         // It's a valid request. Call the low-level routine to do it.
  317.         result = SCSIPrime(pb->ioParam.ioBuffer, vars->driveID, 0, firstPhysicalBlock,
  318.                 &nBlocks, blockSize, PrimeType(pb) == kPrimeWrite, vars->blindOK, kIOTimeout);
  319.         assert(result == noErr, "DTS_SCSI PrimeFunc: SCSIPrime failed");
  320.     } else {
  321.         assert(false, "DTS_SCSI PrimeFunc: requested block(s) not in partition");
  322.         nBlocks = 0;        // no blocks transferred
  323.         result = paramErr;
  324.     }
  325.     
  326.     //
  327.     // Return the actual number of bytes transferred, and move the current position by
  328.     // this amount.
  329.     //
  330.     pb->ioParam.ioActCount = nBlocks * blockSize;
  331.     assert(pb->ioParam.ioActCount == pb->ioParam.ioReqCount,"DTS_SCSI PrimeFunc: Couldn't read entire request");
  332.     dce->dCtlPosition += pb->ioParam.ioActCount;
  333.     
  334.     // a result must be negative, so coerce a positive SCSI status byte to an ioErr
  335.     // *** spruce this up with a comment.
  336.     return result > 0 ? ioErr : result;
  337. }
  338.  
  339.  
  340. //
  341. // Control calls to the driver get vectored to here.
  342. //
  343. OSErr ControlFunc(ParmBlkPtr pb, DCtlPtr dce) {
  344.     TDriveVars    *vars;
  345.     OSErr        result;
  346.     
  347.     //
  348.     // Make sure we got our storage; return an error if we didn't
  349.     //
  350.     if (dce->dCtlStorage == NULL) 
  351.         return notOpenErr;
  352.     vars = *(TDriveVars **) dce->dCtlStorage;
  353.  
  354.     switch (pb->cntrlParam.csCode) {
  355.         case killCode:            
  356.             //
  357.             // We ignore KillIO -- we're completely synchronous.
  358.             // *** we note it during debugging, just for fun.
  359.             assert(false,"ControlFunc: KillIO called, by the way.");
  360.             result = noErr;
  361.             break;
  362.         
  363.         case verifyCode:        
  364.         case formatCode:
  365.             //
  366.             // We ignore Verify and Format; they both return "no problem" as long
  367.             // as the specified drive is ours. (The only way to format a drive is
  368.             // from our formatting application; when the user chooses Erase Disk
  369.             // from the Finder, the driver doesn't need to do anything; DIZero()
  370.             // will "erase" the disk.)
  371.             //
  372.             result = (IsValidDrive(vars, pb->ioParam.ioVRefNum)) ? noErr : nsvErr;
  373.             break;
  374.         
  375.         case ejectCode:
  376.             if (!IsValidDrive(vars, pb->ioParam.ioVRefNum)) {
  377.                 result = nsvErr;
  378.             } else {
  379.                 // 
  380.                 // The eject call for a fixed hard disk is a little bit unusual.  Since 
  381.                 // it doesn't make sense to eject a hard disk, we just behave stubbornly 
  382.                 // and post disk-insert events for ourselves in response.  
  383.                 //
  384.                 // The exception is at boot time, when we may receive an eject call if we 
  385.                 // fail to become the boot volume.  In this case, we must wait until our 
  386.                 // first accRun call to post the disk insert event. 
  387.                 //
  388.                 // No matter what, we return an error.
  389.                 //
  390.                 result = controlErr; // we're going to return controlErr either way.
  391.                 if (IsBootTime()) {
  392.                     // 
  393.                     // The tickleFlag may be set false by a prime call during the boot process.
  394.                     // If, for instance, this volume has no system folder, the boot code may
  395.                     // first read from it then try to eject it like a floppy.  Setting tickleFlag
  396.                     // true reverses the effects of the prime routine's negation of tickleFlag.
  397.                     //
  398.                     vars->tickleFlag = true;
  399.                 } else {
  400.                     // We're not at boot time. Reinsert the drive.
  401.                     // *** I'm not sure that we still need to do this. Dragging the volume
  402.                     //     to the trash needs to work!
  403.                     PostEvent(diskEvt, vars->driveQElem.elem.dQDrive);
  404.                 }
  405.             }
  406.             break;
  407.             
  408.         case mediaIconCode:
  409.         case deviceIconCode:
  410.             //
  411.             // If the drive's valid, return a pointer to our icon & mask in [csParam].
  412.             // The Finder will use this icon for our drive on the desktop. Note that
  413.             // we can only provide a black-and-white icon; only the user can customize
  414.             // the icon and make it colored.
  415.             // The deviceIconCode selector is used for the "physical" icon; this is used
  416.             // by DIBadMount to give an indication of where the disk is (or, under System
  417.             // 6, the Finder will use it if the "Use Physical Icon" bit is set in the
  418.             // LAYO resource.)
  419.             //
  420.             if (IsValidDrive(vars, pb->ioParam.ioVRefNum)) {
  421.                 *(long *) pb->cntrlParam.csParam = DriveIcon;
  422.                 result = noErr;
  423.             } else
  424.                 result = nsvErr;
  425.             break;
  426.                 
  427.         case accRun:
  428.             // 
  429.             // If we aren't the boot device we probably got "ejected" and need this
  430.             // tickle to reassert ourselves and get mounted on the desktop.
  431.             //
  432.             if (vars->tickleFlag && vars->partitionOffset != 0)
  433.                 PostEvent(diskEvt, vars->driveQElem.elem.dQDrive);
  434.             
  435.             //
  436.             // Also, we should check again to see if blind transfers are safe.
  437.             // Since last time we checked, ROM patches have been loaded.
  438.             //
  439.             vars->blindOK = BlindIsSafe();
  440.             
  441.             //
  442.             // Turn off future accRun calls - we don't need 'em anymore.
  443.             // 
  444.             dce->dCtlFlags &= ~(dNeedTime << 8);     // cancel further accRun calls (in high byte of flags)
  445.             vars->tickleFlag = false;                 // clear our insertion indication
  446.             
  447.             result = noErr;
  448.             break;
  449.         
  450.         case goodbye:
  451.             // we don't need a goodbye kiss, but if we did, we'd act on it here.
  452.         default:
  453.             result = controlErr;                            // "undefined csCode"
  454.             break;
  455.     }
  456.     
  457.     return result;
  458. }
  459.  
  460.  
  461. //
  462. // Status calls come here.
  463. //
  464. OSErr StatusFunc(ParmBlkPtr pb, DCtlPtr dce)
  465. {
  466.     TDriveVars    *vars = *(TDriveVars **) dce->dCtlStorage;
  467.     OSErr        result = nsvErr;
  468.     
  469.     if (IsValidDrive(vars, pb->cntrlParam.ioVRefNum))
  470.         switch (pb->cntrlParam.csCode) {
  471.             default:
  472.                 result = statusErr;                            // undefined csCode
  473.                 break;
  474.         }
  475.     
  476.     return result;
  477. }
  478.  
  479. OSErr CloseFunc (ParmBlkPtr pb, DCtlPtr dce)
  480. {
  481. #pragma unused (pb)
  482.  
  483.     TDriveVars    *vars = *(TDriveVars **) dce->dCtlStorage;
  484.     if (vars != NULL) {
  485.         // Dequeue our drive queue element
  486.         Dequeue((QElemPtr) &vars->driveQElem, GetDrvQHdr());
  487.         
  488.         // Dispose of our storage, and forget we ever had it.
  489.         DisposHandle((Handle) vars);
  490.         dce->dCtlStorage = NULL;
  491.         assert(MemError() == noErr, "DTS_SCSI CloseFunc: error disposing dCtlStorage");
  492.     }
  493.     return noErr;
  494. }
  495.  
  496.